#include "server.h"
#include <sstream>
#include <string.h>

namespace  server
{
  //************************************生产者线程入口函数***********************
  void * Product(void * arg)
  {
    printf("Product\n");
    //生产者线程要做的事情是，将收到的消息存入到队列
    Chat_Server * server = reinterpret_cast<Chat_Server *>(arg);
    //注意此处一定要循环的处理事件
    while(true)
    {
      //循环的进行读取数据
      server->Recevice_Msg();
    }
    return NULL;
  }

  //***********************************消费者线程入口函数***********************
  void * Consum(void * arg)
  {
    printf("Consum\n");
    //消费者线程要做的事情是，将队列中的数发送出去
    Chat_Server * server = reinterpret_cast<Chat_Server *>(arg);
    //注意此处一定要循环的处理事件
    while(true)
    {
      //循环的进行读取数据
      server->BoardCastMsg();
    }
    return NULL;
  }

  //1.*********************************启动服务器**********************************
  //***********************************并且进行事件循环****************************
  int  Chat_Server::Start_Server(const std::string &ip,const int & port)
  {
    //启动服务器
    //使用UDP协议作为通信服务器
    _sock = socket(AF_INET,SOCK_DGRAM,0);
    if(_sock < 0)
    {
      perror("socket");
      return -1;
    }

    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    addr.sin_port = htons(port);
    //绑定IP和端口号
    if( bind(_sock,(sockaddr *)&addr,sizeof(addr)) < 0) 
    {
      perror("bind");
      return -2;
    }
    //TODO
    //打印日志，使用util.cpp
    printf("start Ok\n");
    
    //进入事件循环
    //创建两个线程，一个为生产者，一个为消费者
    pthread_t productor ,consumer;

    //因为要处理的数据都是成员变量，所以直接将this传过去
    pthread_create(&productor,NULL,Product,this);
    pthread_create(&consumer,NULL,Consum,this);

    //进行线程等待，避免内存泄露
    pthread_join(productor,NULL);
    pthread_join(consumer,NULL);
    return 0;
  }

  
  //2.*********************************进行接收消息**********************************
  //*********************************************************************************
 int  Chat_Server::Recevice_Msg()
  {
    //(1)从socket中读取消息，每次读取5k大小的数据
    char buf[1024 * 5] ={0};
    sockaddr_in peer;
    socklen_t peer_len = sizeof(peer);
    ssize_t recv_size = recvfrom(_sock,buf,sizeof(buf) -1,0,(sockaddr *)&peer,&peer_len);
    printf("client[%s:%d]say:%s",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);
    if(recv_size < 0)
    {
      perror("read");
      return -3;
    }
    if(recv_size == 0)
    {
      printf("peer closed\n");
      return 0;
    }
    //(2)将读取的数据存入队列中
    context cont;
    cont.str = buf;
    //std::cout<<"cont.buf"<<cont.str<<std::endl;
    cont.addr = peer;
     _block_queue.Block_Queue_Push(cont);
     return 0;
  }


  //3.**********************************进行向所有的好友都发送数据*******************
  //*********************************************************************************
  
 //(3)a)增加新的好友或者更新好友列表
 void Chat_Server::Adduser(const sockaddr_in & addr,const std::string & name)
 {
    //stringstream 对象是将数据流入到一个string对象中
    //而ostream是将数据流入到便准输出上
    //这里使用stringstream 对象来构造key(用户唯一标识信息)
    std::stringstream sstr;
    //key的结构为 zdd:1234444424(ip):9090(port)
    sstr<<name<<":"<<addr.sin_addr.s_addr<<":"<<addr.sin_port;
    //这里利用[]的不存在就插入，存在就更新
    //mapped_type& operator[] ( const key_type& k  );
    //string str() const;
    _online_friends_list[sstr.str()] = addr; 
    printf("peer,:%s\n",inet_ntoa(addr.sin_addr));
 }

 //(3)b)若好友请求下线，就要将将这个好友从_online_friends_list中删除
 void Chat_Server::Deluser(const sockaddr_in & addr , const std::string &name)
 {
   std::stringstream sstr;
   //这里使用stringstream 对象来构造key(用户唯一标识信息)
   sstr<<name<<":"<<addr.sin_addr.s_addr<<":"<<addr.sin_port;
   //size_type erase ( const key_type& k  );
   //string str() const;
   _online_friends_list.erase(sstr.str());
 }

 //(3)c)向一个用户发送消息
  void Chat_Server::Send_Msg(const sockaddr_in & addr,const std::string & str)
 {
   printf("send)msg ok\n");
   sendto(_sock,(void *)str.c_str(),str.size(),0,(sockaddr *)&addr,sizeof(addr)); 
   std::cout<<"send to ["<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<":"<<str;
 }

//*************************************广播消息*************************************  
 int  Chat_Server::BoardCastMsg()
 {
    //(1)从队列中取出消息
    //这个数据结构就是用户的信息
    context cont;
    //TODO
    //下面的参数传的是地址
    _block_queue.Block_Queue_Pop(cont);
    //(2)将消息进行序列化
    server::Msg msg;
    msg.UnSerialize(cont.str);
    //(3)根据消息，更新好友列表

    //a)如果该用户消息quit为以下状态,说明该用户要下线了，就应该将该用户删除
    if(msg.cmd == "quit")
    {
      //删除这个用户，需要根据个用户的唯一标识信息来进行删除
      Deluser(cont.addr,msg.name);
      printf("删除一个用户\n");
    }
    else
    {
      //b)如果这个用户不存在，就要将这个用户存入好友列表中
      //c)如果这个用户存在，就更新
      //其实这里因为好友列表用的是 unordered_map 
      //重载的[]操作为若存在就进行插入，若不存在，就进行更新
      Adduser(cont.addr,msg.name);
    }
    //(4)遍历好友列表，对每个好友都发送消息
    printf("打印好友列表\n");
    printf("**********************\n");
    for(auto item : _online_friends_list)
    {
      std::cout<<item.first<<std::endl;  
    }
    printf("**********************\n");
    for(auto item : _online_friends_list)
    {
      //这里发送的数据可以直接是context种已经序列话好的字符串
      //发送的对象是 value(sockaddr_n)
      Send_Msg(item.second,cont.str);  
    }
  return 0;
 }

}//end server namespace

